1use super::disasm::*;
2use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::encoding::*;
6use anyhow::Result;
7use std::cell::RefCell;
8use std::io::Write;
9use unicode_segmentation::UnicodeSegmentation;
10
11#[derive(Debug)]
12pub struct MesBuilder {}
14
15impl MesBuilder {
16 pub fn new() -> Self {
18 Self {}
19 }
20}
21
22impl ScriptBuilder for MesBuilder {
23 fn default_encoding(&self) -> Encoding {
24 Encoding::Cp932
25 }
26
27 fn build_script(
28 &self,
29 buf: Vec<u8>,
30 _filename: &str,
31 encoding: Encoding,
32 _archive_encoding: Encoding,
33 config: &ExtraConfig,
34 _archive: Option<&Box<dyn Script>>,
35 ) -> Result<Box<dyn Script>> {
36 Ok(Box::new(Mes::new(buf, encoding, config)?))
37 }
38
39 fn extensions(&self) -> &'static [&'static str] {
40 &["mes"]
41 }
42
43 fn script_type(&self) -> &'static ScriptType {
44 &ScriptType::Silky
45 }
46}
47
48struct TextParser<'a> {
49 data: Vec<&'a str>,
50 typ: SlikyStringType,
51 opcodes: &'static Opcodes,
52 encoding: Encoding,
53 pos: usize,
54}
55
56impl<'a> TextParser<'a> {
57 fn new(
58 s: &'a str,
59 typ: SlikyStringType,
60 opcodes: &'static Opcodes,
61 encoding: Encoding,
62 ) -> Self {
63 let data = s.graphemes(true).collect();
64 Self {
65 data,
66 typ,
67 opcodes,
68 encoding,
69 pos: 0,
70 }
71 }
72
73 fn parse(mut self) -> Result<Vec<u8>> {
74 match self.typ {
75 SlikyStringType::Internal => Err(anyhow::anyhow!(
76 "Internal strings cannot be parsed from text."
77 )),
78 SlikyStringType::Name => {
79 let mut m = MemWriter::new();
80 m.write_u8(self.opcodes.push_string)?;
81 let s = encode_string(self.encoding, &self.data.join(""), false)?;
82 m.write_all(&s)?;
83 m.write_u8(0)?;
84 Ok(m.into_inner())
85 }
86 SlikyStringType::Message => {
87 let mut m = MemWriter::new();
88 let mut in_ruby = false;
89 let mut in_normal_text = false;
90 while let Some(c) = self.next() {
91 match c {
92 "[" => {
93 if in_ruby {
94 return Err(anyhow::anyhow!("Nested ruby tags are not allowed."));
95 }
96 if in_normal_text {
97 m.write_u8(0)?;
98 in_normal_text = false;
99 }
100 in_ruby = true;
101 m.write_u8(self.opcodes.escape_sequence)?;
102 m.write_u8(1)?; m.write_u8(self.opcodes.message2)?;
104 }
105 "]" => {
106 if !in_ruby {
107 return Err(anyhow::anyhow!("Unmatched closing ruby tag."));
108 }
109 in_ruby = false;
110 m.write_u8(0)?;
111 m.write_u8(self.opcodes.r#yield)?;
112 }
113 "\n" => {
114 if in_ruby {
115 return Err(anyhow::anyhow!("Newline inside ruby is not allowed."));
116 }
117 if in_normal_text {
118 m.write_u8(0)?;
119 in_normal_text = false;
120 }
121 m.write_u8(self.opcodes.escape_sequence)?;
122 m.write_u8(0)?; }
124 _ => {
125 if !in_ruby && !in_normal_text {
126 in_normal_text = true;
127 m.write_u8(self.opcodes.message2)?;
128 }
129 let s = encode_string(self.encoding, c, false)?;
130 m.write_all(&s)?;
131 }
132 }
133 }
134 if in_ruby {
135 m.write_u8(0)?;
136 m.write_u8(self.opcodes.r#yield)?;
137 }
138 if in_normal_text {
139 m.write_u8(0)?;
140 }
141 Ok(m.into_inner())
142 }
143 }
144 }
145
146 fn next(&mut self) -> Option<&'a str> {
147 if self.pos < self.data.len() {
148 let c = self.data[self.pos];
149 self.pos += 1;
150 Some(c)
151 } else {
152 None
153 }
154 }
155}
156
157#[derive(Debug)]
158pub struct Mes {
159 disasm: RefCell<Box<dyn Disasm>>,
160 encoding: Encoding,
161 texts: Vec<SlikyString>,
162}
163
164impl Mes {
165 pub fn new(buf: Vec<u8>, encoding: Encoding, _config: &ExtraConfig) -> Result<Self> {
166 let reader = MemReader::new(buf);
167 let num_message = reader.cpeek_u32()?;
168 let code_offset = 4 + num_message as u64 * 4;
169 let first_line_offset = reader.cpeek_u32_at(4)? as u64 + code_offset;
170 let mut disasm: Box<dyn Disasm> = if reader.cpeek_u8_at(first_line_offset)? == 0x19
171 && reader.cpeek_u32_at(first_line_offset + 1)? == 0
172 {
173 Box::new(Ai6WinDisasm::new(reader)?)
174 } else {
175 Box::new(PlusDisasm::new(reader)?)
176 };
177 disasm.read_header()?;
178 let texts = disasm.read_code()?;
179 Ok(Self {
180 disasm: RefCell::new(disasm),
181 encoding,
182 texts,
183 })
184 }
185
186 fn code_to_text(&self, str: &SlikyString) -> Result<String> {
187 let mut disasm = self.disasm.try_borrow_mut()?;
188 let mut result = String::new();
189 disasm.stream_mut().pos = str.start as usize;
190 let end = str.start as usize + str.len as usize;
191 let opcodes = disasm.opcodes();
192 while disasm.stream().pos < end {
193 let (opcode, operands) = disasm.read_instruction()?;
194 if opcode == opcodes.push_string
195 || (opcode == opcodes.message1 && !opcodes.is_message1_obfuscated)
196 || opcode == opcodes.message2
197 {
198 if let Some(Obj::Str(s)) = operands.get(0) {
199 let s = disasm.stream().cpeek_fstring_at(
200 s.start,
201 s.len as usize,
202 self.encoding,
203 true,
204 )?;
205 result.push_str(&s);
206 }
207 } else if opcode == opcodes.message1 && opcodes.is_message1_obfuscated {
208 if let Some(Obj::Str(s)) = operands.get(0) {
209 let mut deobfuscated = vec![0u8; (s.len as usize - 1) * 2];
210 let mut input_idx = 0;
211 let mut output_idx = 0;
212 let tlen = s.len - 1;
213 while input_idx < tlen {
214 let b = disasm.stream().cpeek_u8_at(s.start + input_idx)?;
215 input_idx += 1;
216 if matches!(b, 0x81..0xA0 | 0xE0..0xF0) {
217 deobfuscated[output_idx] = b;
218 output_idx += 1;
219 deobfuscated[output_idx] =
220 disasm.stream().cpeek_u8_at(s.start + input_idx)?;
221 input_idx += 1;
222 output_idx += 1;
223 } else {
224 let c = b as i32 - 0x7D62;
225 deobfuscated[output_idx] = (c >> 8) as u8;
226 output_idx += 1;
227 deobfuscated[output_idx] = (c & 0xFF) as u8;
228 output_idx += 1;
229 }
230 }
231 deobfuscated.truncate(output_idx);
232 let s = decode_to_string(self.encoding, &deobfuscated, true)?;
233 result.push_str(&s);
234 }
235 } else if opcode == opcodes.escape_sequence {
236 if let Some(Obj::Byte(e)) = operands.get(0) {
237 match e {
238 0 => result.push('\n'),
240 1 => result.push_str("["),
242 _ => {
243 return Err(anyhow::anyhow!("Unknown escape sequence: {}", e));
244 }
245 }
246 }
247 } else if opcode == opcodes.r#yield {
248 result.push_str("]");
249 }
250 }
251 Ok(result)
252 }
253}
254
255impl Script for Mes {
256 fn default_output_script_type(&self) -> OutputScriptType {
257 OutputScriptType::Json
258 }
259
260 fn default_format_type(&self) -> FormatOptions {
261 FormatOptions::None
262 }
263
264 fn extract_messages(&self) -> Result<Vec<Message>> {
265 let mut messages = Vec::new();
266 let mut name = None;
267 for t in self.texts.iter() {
268 match t.typ {
269 SlikyStringType::Internal => {}
270 SlikyStringType::Name => {
271 name = Some(self.code_to_text(t)?);
272 }
273 SlikyStringType::Message => {
274 let message = self.code_to_text(t)?;
275 messages.push(Message {
276 name: name.take(),
277 message,
278 });
279 }
280 }
281 }
282 Ok(messages)
283 }
284
285 fn import_messages<'a>(
286 &'a self,
287 messages: Vec<Message>,
288 file: Box<dyn WriteSeek + 'a>,
289 _filename: &str,
290 encoding: Encoding,
291 replacement: Option<&'a ReplacementTable>,
292 ) -> Result<()> {
293 let opcodes = self.disasm.try_borrow()?.opcodes();
294 let mut inp = self.disasm.try_borrow()?.stream().clone();
295 inp.pos = 0;
296 let mut patcher = BinaryPatcher::new(inp.to_ref(), file, |add| Ok(add), |add| Ok(add))?;
297 let mut mess = messages.iter();
298 let mut mes = mess.next();
299 for text in &self.texts {
300 patcher.copy_up_to(text.start)?;
301 match text.typ {
302 SlikyStringType::Internal => {}
304 SlikyStringType::Name => {
305 let m = match mes {
306 Some(m) => m,
307 None => {
308 return Err(anyhow::anyhow!("Not enough messages"));
309 }
310 };
311 let mut name = match &m.name {
312 Some(n) => n.to_string(),
313 None => {
314 return Err(anyhow::anyhow!("Message name is missing"));
315 }
316 };
317 if let Some(repl) = replacement {
318 for (k, v) in &repl.map {
319 name = name.replace(k, v);
320 }
321 }
322 let data =
323 TextParser::new(&name, SlikyStringType::Name, opcodes, encoding).parse()?;
324 patcher.replace_bytes(text.len, &data)?;
325 }
326 SlikyStringType::Message => {
327 let m = match mes {
328 Some(m) => m,
329 None => {
330 return Err(anyhow::anyhow!("Not enough messages"));
331 }
332 };
333 let mut message = m.message.to_string();
334 if let Some(repl) = replacement {
335 for (k, v) in &repl.map {
336 message = message.replace(k, v);
337 }
338 }
339 let data =
340 TextParser::new(&message, SlikyStringType::Message, opcodes, encoding)
341 .parse()?;
342 patcher.replace_bytes(text.len, &data)?;
343 mes = mess.next();
344 }
345 }
346 }
347 if mes.is_some() || mess.next().is_some() {
348 return Err(anyhow::anyhow!("Too many messages"));
349 }
350 patcher.copy_up_to(inp.data.len() as u64)?;
351 let code_offset = self.disasm.try_borrow()?.code_offset();
352 for &address_offset in self.disasm.try_borrow()?.little_endian_addresses() {
353 let orig_address = inp.cpeek_u32_at(address_offset as u64)? as u64;
354 let orig_offset = orig_address + code_offset as u64;
355 let new_offset = patcher.map_offset(orig_offset)?;
356 let new_address = new_offset - code_offset as u64;
357 patcher.patch_u32(address_offset as u64, new_address as u32)?;
358 }
359 for &address_offset in self.disasm.try_borrow()?.big_endian_addresses() {
360 let orig_address = inp.cpeek_u32_be_at(address_offset as u64)? as u64;
361 let orig_offset = orig_address + code_offset as u64;
362 let new_offset = patcher.map_offset(orig_offset)?;
363 let new_address = new_offset - code_offset as u64;
364 patcher.patch_u32_be(address_offset as u64, new_address as u32)?;
365 }
366 Ok(())
367 }
368}
369
370#[test]
371fn test_text_parser() {
372 let opcodes = &PLUS_OPCODES;
373 let parser = TextParser::new(
374 "Hello, [world]s\nThis is a test.",
375 SlikyStringType::Message,
376 opcodes,
377 Encoding::Utf8,
378 );
379 let data = parser.parse().unwrap();
380 assert_eq!(
381 data,
382 vec![
383 opcodes.message2,
384 b'H',
385 b'e',
386 b'l',
387 b'l',
388 b'o',
389 b',',
390 b' ',
391 0,
392 opcodes.escape_sequence,
393 1,
394 opcodes.message2,
395 b'w',
396 b'o',
397 b'r',
398 b'l',
399 b'd',
400 0,
401 opcodes.r#yield,
402 opcodes.message2,
403 b's',
404 0,
405 opcodes.escape_sequence,
406 0,
407 opcodes.message2,
408 b'T',
409 b'h',
410 b'i',
411 b's',
412 b' ',
413 b'i',
414 b's',
415 b' ',
416 b'a',
417 b' ',
418 b't',
419 b'e',
420 b's',
421 b't',
422 b'.',
423 0
424 ]
425 );
426}